<?
/**
 * Session for sending API requests to a service supported
 * by the Large Merchant Services Platform.
 * 
 * As of the writing of this documentation this includes:
 * (1) Bulk Data Exchange Service (BDX)
 * (2) File Transfer Service (FTS) 
 */
class LargeMerchantServiceSession extends gravy{
	
	private $dataFormat="XML";
	private $responseDataFormat="XML";
	public $securityToken="";
	public $bulkDataExchangeServiceEndpoint="";
	public $uuid=false;
	public $jobType="";
	public $fileReferenceId="";
	public $inputFileReferenceId="";
	public $percentComplete=0;
	public $jobStatus="";
	public $errorId="";

	public function __construct($configFile){
		parent::__construct();
		$this->__parseConfig($configFile);
	}

	private function __parseConfig($configFile){
		$cfg = parse_ini_file($configFile);
		if ($cfg == false) {
//			$this->LogMsg("config file not found", 0, E_ERROR);
		}else{
			$this->setAppMode($cfg['app-mode']);
		}
	}

	public function setAppMode($appMode){
		if($appMode==1){
			$this->bulkDataExchangeServiceEndpoint="https://webservices.sandbox.ebay.com/BulkDataExchangeService";
			$this->fileTransferServiceEndpoint="https://storage.sandbox.ebay.com/FileTransferService";
		}else{
			$this->bulkDataExchangeServiceEndpoint="https://webservices.ebay.com/BulkDataExchangeService";
			$this->fileTransferServiceEndpoint="https://storage.ebay.com/FileTransferService";
		}
	}

	public function setAuthToken($authToken){
		$this->securityToken=$authToken;
	}

	/**
	 * Parses for the Error Message in the MIME multipart message.
	 * @param $response MIME multipart message
	 * @return string XML Error Message Response
	 */
	private function __parseForErrorMessage($response){
		$beginErrorMessage=strpos($response,'<?xml');
		$endErrorMessage=strpos($response, '</errorMessage>',$beginErrorMessage);
		$endErrorMessage+=strlen('</errorMessage>');
		return(substr($response,$beginErrorMessage,$endErrorMessage-$beginErrorMessage));
	}

	/**
	 * Parses for the XML Response in the MIME multipart message.
	 * @param string $response MIME multipart message
	 * @return string XML Response
	 */
	private function __parseForResponseXML($response){
		$beginResponseXML=strpos($response,'<?xml');
		$endResponseXML=strpos($response,'</downloadFileResponse>',$beginResponseXML);
		//Assume a service level error and die.
		if($endResponseXML===FALSE){
			$errorXML=$this->__parseForErrorMessage($response);
			$this->addCustomError("0xLMSPX0","Error Parsing XML Response","An error occurred while trying to parse the XML response.",array("errorXML"=>$errorXML,"jobID"=>$this->jobId,"fileReferenceId"=>$this->fileReferenceId));
		}
		$endResponseXML+=strlen('</downloadFileResponse>');
		return(substr($response,$beginResponseXML,$endResponseXML-$beginResponseXML));
	}

	/**
	 * Parses for the file bytes between the MIME boundaries.
	 * @param $uuid UUID corresponding to the Content-ID of the file bytes.
	 * @param string $response MIME multipart message
	 * @return string bytes of the file
	 */
	private function __parseForFileBytes($uuid,$response){
		$contentId='Content-ID: <'.$uuid.'>';
		$mimeBoundaryPart=strpos($response,'--MIMEBoundaryurn_uuid_');
		$beginFile=strpos($response,$contentId,$mimeBoundaryPart);
		$beginFile+=strlen($contentId);
		//Accounts for the standard 2 CRLFs.
		$beginFile+=4;
		$endFile=strpos($response,'--MIMEBoundaryurn_uuid_',$beginFile);
		//Accounts for the standard 1 CRLFs.
		$endFile-=2;
		$fileBytes=substr($response,$beginFile,$endFile-$beginFile);
		return($fileBytes);
	}

	/**
	 * Parses the XML Response for the UUID to ascertain the
	 * index of the file bytes in the MIME Message.
	 * @param DomDocument $responseDOM DOM of the XML Response.
	 * @return string UUID referring to the message body
	 */
	private function __parseForXopIncludeUUID($responseDOM){
		$xopInclude=$responseDOM->getElementsByTagName('Include')->item(0);
		if($xopInclude){
			$uuid=$xopInclude->getAttributeNode('href')->nodeValue;
			$uuid=substr($uuid,strpos($uuid,'urn:uuid:'));
		}else{
			$uuid=false;
		}
		return($uuid);
	}

	/**
	 * Writes the response file's bytes to disk.
	 * @param string $bytes bytes comprising a file
	 * @param string $zipFilename name of the zip to be created
	 */
	public function writeZipFile($bytes,$zipFilename){
		$result=false;
		if($handler=fopen($zipFilename,'wb')){
			$result=fwrite($handler,$bytes);
			fclose($handler);
		}else{
			$this->addCustomError("0xLMSWZ0","Error Creating Zip File","An error occurred while trying to create a ZIP file.",array("jobID"=>$this->jobId,"fileReferenceId"=>$this->fileReferenceId,"zipFilename"=>$zipFilename));
		}
		return($result);
	}

	/**
	 * Get a Job Status Request
	 * @param string $bytes bytes comprising a file
	 * @param string $zipFilename name of the zip to be created
	 */
	public function getJobStatus($jobId=NULL){
		$result=false;
		$jobId=($jobId===NULL)?$this->jobId:$jobId;
		$request='<getJobStatusRequest xmlns:sct="http://www.ebay.com/soaframework/common/types" xmlns="http://www.ebay.com/marketplace/services">';
		$request.='<jobId>'.$jobId.'</jobId>';
		$request.='</getJobStatusRequest>';
		$response=$this->BulkDataExchangeRequest('getJobStatus',$request);
		$xml=simplexml_load_string($response);
		if(!empty($xml)&&'Success'==(string)$xml->ack){
			$this->jobId=(string)$xml->jobProfile->jobId;
			$this->inputFileReferenceId=(string)$xml->jobProfile->inputFileReferenceId;
			$this->fileReferenceId=(string)$xml->jobProfile->fileReferenceId;
			$this->jobStatus=(string)$xml->jobProfile->jobStatus;
			$this->percentComplete=(double)$xml->jobProfile->percentComplete;
			$result=true;
		}
		return($result);
	}

	/**
	 * Get Jobs
	 * http://developer.ebay.com/devzone/bulk-data-exchange/CallRef/types/GetJobsRequest.html
	 * @param $dateFrom dateTime - the date to pull jobs from.
	 * @param $dateTo dateTime - the date to pull jobs to.
	 * @param $jobType token - the job type.
	 * @param $jobStatus token - the job status.
	 */
	public function getJobs($dateFrom=NULL,$dateTo=NULL,$jobType=NULL,$jobStatus=NULL){
		$result=false;
		$jobType=($jobType===NULL)?"":'<jobType>'.$jobType.'</jobType>';
		$jobStatus=($jobStatus===NULL)?"":'<jobStatus>'.$jobStatus.'</jobStatus>';
		$dateFrom=($dateFrom===NULL)?"":'<creationTimeFrom>'.$dateFrom.'</creationTimeFrom>';
		$dateTo=($dateTo===NULL)?"":'<creationTimeTo>'.$dateTo.'</creationTimeTo>';
		$request='<getJobsRequest xmlns:sct="http://www.ebay.com/soaframework/common/types" xmlns="http://www.ebay.com/marketplace/services">';
		$request.=$jobType;
		$request.=$jobStatus;
		$request.=$dateFrom;
		$request.=$dateTo;
		$request.='</getJobsRequest>';
		$response=$this->BulkDataExchangeRequest('getJobs',$request);
		$xml=simplexml_load_string($response);
//		$this->debug("Request",$request);
//		$this->debug("Result",$xml->asXML());
		if(!empty($xml)&&'Success'==(string)$xml->ack){
			$result=$xml;
		}
		return($result);
	}

	/**
	 * Submit a request to Abort a Job
	 * @param string $jobId - The ID of the Job that is to be aborted
	 */
	public function abortJobRequest($jobId=NULL){
		$result=false;
		$jobId=($jobId===NULL)?$this->jobId:$jobId;
		$request='<abortJobRequest xmlns="http://www.ebay.com/marketplace/services">';
		$request.='<jobId>'.$jobId.'</jobId>';
		$request.='</abortJobRequest>';
		$response=$this->BulkDataExchangeRequest('abortJob',$request);
		$xml=simplexml_load_string($response);
		if(!empty($xml)&&'Success'==(string)$xml->ack){
			$result=true;
		}
		return($result);
	}

	public function createUploadJobRequest($jobType){
		$result=false;
		$this->jobType=$jobType;
		$this->uuid=uniqid('', true);
		$request='<createUploadJobRequest xmlns:sct="http://www.ebay.com/soaframework/common/types" xmlns="http://www.ebay.com/marketplace/services">';
		$request.='<uploadJobType>'.$jobType.'</uploadJobType>';
		$request.='<UUID>'.$this->uuid.'</UUID>';
		$request.='</createUploadJobRequest>';
		$response=$this->BulkDataExchangeRequest("createUploadJob",$request);
		if($xml=@simplexml_load_string($response)){
			if(!empty($xml)&&((string)$xml->ack=="Success"||(string)$xml->ack=="Warning")){
				$this->jobId=(string)$xml->jobId;
				$this->inputFileReferenceId=(string)$xml->fileReferenceId;
				$result=true;
			}else{
				$this->errorId=(int)$xml->errorMessage->error[0]->errorId;
				$this->addCustomError("0xLMSCU1","Error Creating Upload File Request","An error occurred while trying to creating an upload file request.",array("xml"=>$xml,"jobType"=>$this->jobType,"jobID"=>$this->jobId,"fileReferenceId"=>$this->fileReferenceId));
			}
		}else{
			$this->addCustomError("0xLMSCU0","Error Creating Upload File Request","An error occurred while trying to creating an upload file request.",array("xml"=>$xml,"jobType"=>$this->jobType,"jobID"=>$this->jobId,"fileReferenceId"=>$this->fileReferenceId));
		}
		return($result);
	}

	public function uploadFileRequest($jobId=NULL,$inputFileReferenceId=NULL,$data=NULL){
		$result=false;
		$jobId=($jobId===NULL)?$this->jobId:$jobId;
		$inputFileReferenceId=($inputFileReferenceId===NULL)?$this->inputFileReferenceId:$inputFileReferenceId;
		$this->jobId=$jobId;
		$request='<uploadFileRequest xmlns:sct="http://www.ebay.com/soaframework/common/types" xmlns="http://www.ebay.com/marketplace/services">';
		$request.='<taskReferenceId>'.$this->jobId.'</taskReferenceId>';
		$request.='<fileReferenceId>'.$this->inputFileReferenceId.'</fileReferenceId>';
		$request.='<fileFormat>gzip</fileFormat>';
		$request.='<fileAttachment>';
		$request.='<Size>'.strlen($data).'</Size>';
		$request.='<Data><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:'.MultiPartMessage::$URN_UUID_ATTACHMENT.'" /></Data>';
		$request.='</fileAttachment>';
		$request.='</uploadFileRequest>';

		$request=MultiPartMessage::build($request,$data);

		$response=$this->FileTransferServiceUploadRequest($request);
		if($xml=@simplexml_load_string($response)){
			if(!empty($xml)&&((string)$xml->ack=="Success"||(string)$xml->ack=="Warning")){
				$result=true;
			}else{
				$this->addCustomError("0xLMSUF1","Error Uploading File","An error occurred while trying to upload a file with a request.",array("xml"=>$xml,"jobType"=>$this->jobType,"jobID"=>$this->jobId,"inputFileReferenceId"=>$this->inputFileReferenceId));
			}
		}else{
			$this->addCustomError("0xLMSUF0","Error Uploading File","An error occurred while trying to upload a file with a request.",array("xml"=>$xml,"response"=>$response,"jobType"=>$this->jobType,"jobID"=>$this->jobId,"inputFileReferenceId"=>$this->inputFileReferenceId));
		}
		return($result);
	}

	public function startUploadJobRequest($jobId=NULL){
		$result=false;
		$jobId=($jobId===NULL)?$this->jobId:$jobId;
		$this->jobId=$jobId;
		$request='<startUploadJobRequest xmlns:sct="http://www.ebay.com/soaframework/common/types" xmlns="http://www.ebay.com/marketplace/services">';
		$request.='<jobId>'.$jobId.'</jobId>';
		$request.='</startUploadJobRequest>';
		$response=$this->BulkDataExchangeRequest("startUploadJob",$request);
		if($xml=@simplexml_load_string($response)){
			if(!empty($xml)&&((string)$xml->ack=="Success"||(string)$xml->ack=="Warning")){
				$result=true;
			}else{
				$this->addCustomError("0xLMSSU1","Error Starting Upload Job","An error occurred while trying to start an upload job request.",array("xml"=>$xml,"jobType"=>$this->jobType,"jobID"=>$this->jobId,"fileReferenceId"=>$this->fileReferenceId));
			}
		}else{
			$this->addCustomError("0xLMSSU0","Error Starting Upload Job","An error occurred while trying to start an upload job request.",array("xml"=>$xml,"jobType"=>$this->jobType,"jobID"=>$this->jobId,"fileReferenceId"=>$this->fileReferenceId));
		}
		return($result);
	}

	public function performUploadJobRequest($jobType,$data){
		$result=false;
		$this->jobType=$jobType;
		if($this->createUploadJobRequest($jobType)){
			if($this->uploadFileRequest(NULL,NULL,$data)){
				if($this->startUploadJobRequest()){
					$result=true;
				}else{
					$this->addCustomError("0xLMSUJ0","Error Performing Upload Job","An error occurred while trying to start an upload job request.",array("jobType"=>$jobType,"jobID"=>$this->jobId,"fileReferenceId"=>$this->fileReferenceId));
				}
			}else{
				$this->addCustomError("0xLMSUJ1","Error Performing Upload Job","An error occurred while trying to upload a file for the job.",array("jobType"=>$jobType,"jobID"=>$this->jobId,"fileReferenceId"=>$this->fileReferenceId));
			}
		}else{
			$this->addCustomError("0xLMSUJ2","Error Performing Upload Job","An error occurred while trying to create an upload job request.",array("jobType"=>$jobType,"jobID"=>$this->jobId,"fileReferenceId"=>$this->fileReferenceId));
		}
		return($result);
	}

	public function startDownloadJob(){
		$result=false;
		$request='<?xml version="1.0" encoding="utf-8"?>';
		$request.='<startDownloadJobRequest xmlns="http://www.ebay.com/marketplace/services">';
		$request.='<downloadJobType>'.$this->jobType.'</downloadJobType>';
		$request.='<UUID>'.uniqid("",true).'</UUID>';
		$request.='</startDownloadJobRequest>';
		$response=$this->BulkDataExchangeRequest("startDownloadJob",$request);
		if($xml=@simplexml_load_string($response)){
			if(!empty($xml)&&((string)$xml->ack=="Success"||(string)$xml->ack=="Warning")){
				$this->jobId=(string)$xml->jobId;
				$this->fileReferenceId=(string)$xml->fileReferenceId;
				$result=true;
			}else{
				$this->addCustomError("0xLMSSD1","Error Starting Download Job","An error occurred while trying to start a download job request.",array("xml"=>$xml,"jobType"=>$this->jobType,"jobID"=>$this->jobId));
			}
		}else{
			$this->addCustomError("0xLMSSD0","Error Starting Download Job","An error occurred while trying to start a download job request.",array("xml"=>$xml,"jobType"=>$this->jobType,"jobID"=>$this->jobId));
		}
		return($result);
	}

	public function performDownloadRequest($jobType){
		$result=false;
		$this->jobType=$jobType;
		if($this->startDownloadJob($jobType)){
			$result=true;
		}else{
			$this->addCustomError("0xLMSDJ0","Error Performing Download Request","An error occurred while trying to create download job request.",array("jobType"=>$jobType,"jobID"=>$this->jobId));
		}
		return($result);
	}

	public function downloadFile($jobId,$saveFileName){
		$result=false;
		if($this->getJobStatus($jobId)){
			$request='<downloadFileRequest xmlns:sct="http://www.ebay.com/soaframework/common/types" xmlns="http://www.ebay.com/marketplace/services">';
			$request.='	<taskReferenceId>'.$jobId.'</taskReferenceId>';
			$request.='	<fileReferenceId>'.$this->fileReferenceId.'</fileReferenceId>';
			$request.='</downloadFileRequest>';
			$response=$this->FileTransferServiceDownloadRequest($request);
			$responseXML=$this->__parseForResponseXML($response);
			$responseDOM=DOMUtils::createDOM($responseXML);
			if($uuid=$this->__parseForXopIncludeUUID($responseDOM)){
				$fileBytes=$this->__parseForFileBytes($uuid,$response);
				$result=$this->writeZipFile($fileBytes,$saveFileName);
			}else{
				$this->addCustomError("0xLMSDF0","Error Performing Download Request","An error occurred while trying to read the download file.",array("jobId"=>$jobId,"saveFileName"=>$this->saveFileName));
			}
		}
		return($result);
	}

	/**
	 * Sends a Bulk Data Exchange Request.
	 * @param string $operationName See http://developer.ebay.com/DevZone/bulk-data-exchange/CallRef/index.html for a list of available operations.
	 * @param string $body XML Request
	 * @return string XML Response
	 */
	public function BulkDataExchangeRequest($operationName, $body){
		print("<h1>LargeMerchantServiceSession::BulkDataExchangeRequest: ".$this->securityToken."</h1>");
		$request=new BulkDataExchangeRequest($this->dataFormat,$this->responseDataFormat,$this->securityToken,$operationName,$this->bulkDataExchangeServiceEndpoint);
		$response=$request->sendHTTPRequest($body);
		return($response);
	}
	
	/**
	 * Sends a File Transfer Service Upload Request.
	 * @param string $body XML Request
	 * @return string XML Response
	 */
	public function FileTransferServiceUploadRequest($body){
		$request=new FileTransferServiceUploadRequest($this->dataFormat,$this->responseDataFormat,$this->securityToken,'uploadFile',$this->fileTransferServiceEndpoint);
		$response = $request->sendHTTPRequest($body);
		return($response);
	}
	
	/**
	 * Sends a File Transfer Service Download Request.
	 * @param string $body XML Request
	 * @return string MIME multipart message.
	 */
	public function FileTransferServiceDownloadRequest($body){
		$request = new FileTransferServiceDownloadRequest($this->dataFormat,$this->responseDataFormat,$this->securityToken,'downloadFile',$this->fileTransferServiceEndpoint);
		$response = $request->sendHTTPRequest($body);
		return($response);
	}
}
?>